package com.fulllife.food.service.impl;

import com.fulllife.food.entity.FoodEntity;
import com.fulllife.food.entity.MenuEntity;
import com.fulllife.food.entity.RecentEntity;
import com.fulllife.food.enums.FoodTypeDetailEnum;
import com.fulllife.food.enums.FoodTypeEnum;
import com.fulllife.food.enums.MealTypeEnum;
import com.fulllife.food.foodstrategy.Analyse;
import com.fulllife.food.foodstrategy.FoodStrategy;
import com.fulllife.food.foodstrategy.MeatStrategy;
import com.fulllife.food.foodstrategy.RangeMenu;
import com.fulllife.food.service.FoodService;
import lombok.extern.slf4j.Slf4j;
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
import org.hswebframework.ezorm.rdb.operator.dml.query.SortOrder;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class FoodServiceImpl extends GenericReactiveCrudService<FoodEntity, String> implements FoodService {

    @Resource
    MeatStrategy meatStrategy;

    @Resource
    private ReactiveRepository<RecentEntity, String> recentDao;

    @Resource
    private ReactiveRepository<MenuEntity, String> menuDao;

    @Override
    public Mono<Analyse> analyze(FoodStrategy foodStrategy) {
        // 菜单
        final Flux<RecentEntity> fetch1 = recentDao.createQuery().fetch().take(21);
        // 菜品
        return meatStrategy.analyze(
                fetch1.flatMap(recentEntity -> getRepository()
                        .createQuery()
                        .is(FoodEntity::getName, recentEntity.getName())
                        .fetch())
        );
    }

    @Override
    public Flux<FoodEntity> suggestion(FoodStrategy foodStrategy) {
        final Flux<RecentEntity> recentEntity = recentDao.createQuery().fetch().take(3);

        final Flux<FoodEntity> foodEntity = recentEntity.flatMap(entity ->
                getRepository()
                        .createQuery()
                        .is(FoodEntity::getName, entity.getName())
                        .orderBy(SortOrder.desc(FoodEntity::getCreateTime))
                        .fetch()
        );
        return foodEntity.collectList()
                .log()
                .map(foodEntities -> {
                    Set<FoodTypeEnum> part = foodEntities.stream()
                            .map(f -> FoodTypeEnum.valueOf(f.getFoodType()))
                            .collect(Collectors.toSet());
                    Set<FoodTypeEnum> all = Arrays.stream(FoodTypeEnum.values()).collect(Collectors.toSet());

                    all.removeAll(part);
                    return all;
                })
                .filter(suggestType -> !CollectionUtils.isEmpty(suggestType))
                .flatMapMany(suggestionType -> getRepository()
                        .createQuery()
                        .in(FoodEntity::getFoodType, suggestionType)
                        .fetch()
                );
    }

    @Override
    public Flux<MenuEntity> range() {
        Mono<MenuEntity> meatMenu = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.big_meat);
        Mono<MenuEntity> halfMeatMenu = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.little_meat);
        Mono<MenuEntity> vegetableMenu = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.vegetable);

        Flux<MenuEntity> menuEntityFlux = meatMenu.concatWith(halfMeatMenu);

        return menuEntityFlux.concatWith(vegetableMenu);
    }

    @Override
    public Mono<RangeMenu> rangeDay() {
        // 早餐
        Mono<MenuEntity> breadMenu = rangeMenu(MealTypeEnum.breakfast, FoodTypeDetailEnum.bread);
        Mono<MenuEntity> milkMenu = rangeMenu(MealTypeEnum.breakfast, FoodTypeDetailEnum.milk);
        Mono<MenuEntity> cakeMenu = rangeMenu(MealTypeEnum.breakfast, FoodTypeDetailEnum.cake);
        Mono<MenuEntity> sausageMenu = rangeMenu(MealTypeEnum.breakfast, FoodTypeDetailEnum.sausage);

        // 午餐
        Mono<MenuEntity> meatMenuLunch = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.big_meat);
        Mono<MenuEntity> halfMeatMenuLunch = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.little_meat);
        Mono<MenuEntity> vegetableMenuLunch = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.vegetable);

        // 晚餐
        Mono<MenuEntity> meatMenuDinner = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.big_meat);
        Mono<MenuEntity> halfMeatMenuDinner = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.little_meat);
        Mono<MenuEntity> vegetableMenuDinner = rangeMenu(MealTypeEnum.lunch, FoodTypeDetailEnum.vegetable);

        Mono<List<MenuEntity>> breakfastMenu = breadMenu.concatWith(milkMenu).concatWith(cakeMenu).concatWith(sausageMenu).take(4).collectList();
        Mono<List<MenuEntity>> lunchMenu = meatMenuLunch.concatWith(halfMeatMenuLunch).concatWith(vegetableMenuLunch).take(3).collectList();
        Mono<List<MenuEntity>> dinnerMenu = meatMenuDinner.concatWith(halfMeatMenuDinner).concatWith(vegetableMenuDinner).take(3).collectList();

        Mono<RangeMenu> rangeMenuMono = Mono.just(new RangeMenu());

        return rangeMenuMono.zipWith(breakfastMenu, (a, b) -> {
            a.setBreakfast(b);
            return a;
        }).zipWith(lunchMenu, (a, c) -> {
            a.setLunch(c);
            return a;
        }).zipWith(dinnerMenu, (a,d) -> {
            a.setDinner(d);
            return a;
        });
    }

    private Mono<MenuEntity> rangeMenu(MealTypeEnum mealType, FoodTypeDetailEnum type) {
        return menuDao.createQuery()
                .is(MenuEntity::getFoodTypeDetail, type.getValue())
                .$like$(MenuEntity::getMealType, mealType.getValue())
                .count()
                .log()
                .flatMap(c -> {
                    System.out.printf("c=%s", c);
                    QueryParamEntity queryParam = new QueryParamEntity();
                    String where = String.format("foodTypeDetail = %s and mealType like %%%s%%", type.getValue(), mealType.getValue());
                    queryParam.setWhere(where);
                    queryParam.setPageSize(1);
                    queryParam.setPageIndex(new Random().nextInt(c));
                    queryParam.setTotal(c);

                    return menuDao.createQuery().setParam(queryParam).fetch().take(1).collectList().map(c1 -> c1.get(0));
                });
    }
}
